﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using lib_nrf24lu1_RF;
using System.Timers;          // Library for RFUSB functions

namespace Send_RF
{
    public partial class SendForm : Form
    {
        //*******************************************************************
        //      RF variables
        //*******************************************************************
        private RfUsb rfusb;
        Details d;

        //*******************************************************************
        //      Timer to perform updates to mOways
        //*******************************************************************
        private System.Timers.Timer myTimer = new System.Timers.Timer();
        private System.Timers.Timer myTimer2 = new System.Timers.Timer();

        //*******************************************************************
        //Mode
        //*******************************************************************
        private bool autom = true;
        private bool broadcast = true;
        private bool transmission = false;
        private int receiveMsg = 10;

        //*******************************************************************
        //      SPEED CONTROLLING
        //*******************************************************************
        private int speedJump = 10;
        private int currentSpeed = 0;
        private bool adding = true;
        private int intervalInMillis = 2000;
        public static int MAX_SPEED = 100;
        public static int MIN_SPEED = 10;


        //Testing
        public static int TIME_TEST = 2000;
        public static int SIZE_TEST = 10;
        public static int SIZE_MOWAYS = 4;
        private RFData[,] RfDataSet = new RFData[SIZE_MOWAYS, SIZE_TEST];
        static Random generator = new Random();
        private System.Timers.Timer myTimer3 = new System.Timers.Timer();
        private int seqToSend = 0;
        private int startTick = 0;
        private int mCount = 0;


        //*******************************************************************
        //      Initialization of RFUSB and application window
        //*******************************************************************
        public SendForm()
        {
            InitializeComponent();

            this.cb_Pckts.SelectedItem = "5";
            this.cb_Speed.SelectedItem = "300";
            this.cb_W8Time.SelectedItem = "0";

            this.rfusb = new RfUsb();
            this.rfusb.TurnOn();

            //Timer
            myTimer.Elapsed += new ElapsedEventHandler(this.updateSpeed);
            myTimer.Interval = intervalInMillis;


            //myTimer2.Elapsed += new ElapsedEventHandler(this.updateMowayList);
            myTimer2.Interval = intervalInMillis;
            //myTimer2.Enabled = true;
            //myTimer2.Start();
            this.btnRfon_Click(null, null);

            //Testing
            this.CreateRfData();
            myTimer3.Elapsed += new ElapsedEventHandler(this.sendPackt);
            myTimer3.Interval = TIME_TEST;


        }

        //*******************************************************************
        //      Stop RFUSB
        //*******************************************************************
        private void btnRfOff_Click(object sender, EventArgs e)
        {
            this.rfusb.turnOff();
            this.setStartPanel(true);
        }

        //*******************************************************************
        //      Start RFUSB (MowayGUI must be closed)
        //*******************************************************************
        private void btnRfon_Click(object sender, EventArgs e)
        {
            this.rfusb.TurnOn();
            this.setStartPanel(false);
        }

        private void setStartPanel(bool enabledState)
        {
            this.nudChannel.Enabled = enabledState;
            this.nudRfusbAddress.Enabled = enabledState;
            this.btnRfOff.Enabled = !enabledState;
            this.btnRfon.Enabled = enabledState;
        }

        //*******************************************************************
        //      Send data from PC to Moway
        //*******************************************************************
        private void btnSend_Click(object sender, EventArgs e)
        {
            //Check if it is a valid number
            this.startInteraction();

        }

        private void startInteraction(int speed)
        {
            if (this.broadcast)
            {
                this.rfusb.Send(new byte[] { Convert.ToByte(speed) });
            }
            else
            {
                try
                {
                    int dirMoway = Convert.ToInt16(cb_Receivers.Text);
                    this.rfusb.Send(dirMoway, new byte[] { Convert.ToByte(speed) });
                }
                catch (Exception e)
                {
                    System.Diagnostics.Debug.WriteLine("-- NO MOWAY SELECTED --");
                }
            }
        }

        private void startInteraction()
        {
            this.startInteraction(this.currentSpeed);
        }

        private void updateMowayList(object source, ElapsedEventArgs e)
        {
            this.receiveMsg = 10;
            IEnumerator<Moway> it = this.rfusb.getMowaysIterator();
            if (it != null)
            {
                Object sel = null;
                this.Invoke((MethodInvoker)delegate
                {
                    sel = this.cb_Receivers.SelectedValue; // runs on UI thread
                    this.cb_Receivers.DataSource = null;
                });


                List<ComboboxItem> list = new List<ComboboxItem>();

                while ((it.MoveNext()) && (it.Current != null))
                {
                    list.Add(new ComboboxItem(it.Current.Id.ToString(), it.Current.Id));
                }
                this.Invoke((MethodInvoker)delegate
                {
                    if (list.Count > 0)
                    {
                        this.cb_Receivers.DataSource = list;
                        if (sel != null)
                        {
                            this.cb_Receivers.SelectedValue = sel;
                        }
                    }
                    this.cb_Receivers.Update();
                });
            }
        }

        private void updateSpeed(object source, ElapsedEventArgs e)
        {

            if (this.transmission && this.autom && this.rfusb.Active() && this.rfusb.SizeConvoy() > 0)
            {
                if (adding)
                {
                    currentSpeed = Math.Min(currentSpeed + speedJump, MAX_SPEED);
                }
                else
                {
                    currentSpeed = Math.Max(currentSpeed - speedJump, MIN_SPEED);
                }

                if (currentSpeed == MIN_SPEED)
                {
                    adding = true;
                }
                else if (currentSpeed == MAX_SPEED)
                {
                    adding = false;
                }

                this.startInteraction();
            }
        }


        /// <summary>
        /// Closing the window also deactivates de RFUSB
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SendForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.rfusb.turnOff();
        }

        private void rdb_Auto_CheckedChanged(object sender, EventArgs e)
        {
            this.gb_SendData.Enabled = false;
            this.autom = true;
            myTimer.Enabled = true;
            myTimer.Start();
        }

        private void rdb_Manual_CheckedChanged(object sender, EventArgs e)
        {
            this.gb_SendData.Enabled = true;
            this.autom = false;
            myTimer.Stop();
        }

        private void rb_Bcast_CheckedChanged(object sender, EventArgs e)
        {
            this.cb_Receivers.Enabled = false;
            this.broadcast = true;
        }

        private void rb_SendDir_CheckedChanged(object sender, EventArgs e)
        {
            this.cb_Receivers.Enabled = true;
            this.broadcast = false;
        }

        public class ComboboxItem
        {
            public string Text { get; set; }
            public object Value { get; set; }

            public ComboboxItem(String txt, Object vle)
            {
                this.Text = txt;
                this.Value = vle;
            }

            public override string ToString()
            {
                return Text;
            }
        }

        private void btn_update_Click(object sender, EventArgs e)
        {
            this.updateMowayList(sender, null);
        }

        private void btn_ManualSend_Click(object sender, EventArgs e)
        {
            int spd = 0;
            try
            {
                spd = Convert.ToInt16(this.tb_Data0.Text);

            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(" -- WRONG SPEED SETTING to 0 --");
                this.tb_Data0.Text = "0";
            }
            finally
            {
                this.startInteraction(spd);
            }
        }

        private void btnDetails_Click(object sender, EventArgs e)
        {
            this.Invoke((MethodInvoker)delegate
            {
                if (d == null)
                {
                    d = new Details();
                }
                d.OpenWindow();
            });

        }

        private void CreateRfData()
        {
            for (int m = 0; m < SIZE_MOWAYS; m++)
            {
                for (int i = 0; i < SIZE_TEST; i++)
                {
                    this.RfDataSet[m, i] = new RFData(i);
                }
            }
        }

        private void updateLog(string fileName, int limit)
        {
            if (!myTimer3.Enabled && mCount > 0)
            {
                int[,][] matrix = this.rfusb.DataTest();
                for (int i = 0; i < SIZE_MOWAYS; i++)
                {
                    for (int j = 0; j < SIZE_TEST; j++)
                    {
                        if (matrix[i, j] != null)
                        {
                            RfDataSet[i, j].TimeAck = matrix[i, j][1];
                            RfDataSet[i, j].Ack = matrix[i, j][2];
                            //System.Diagnostics.Debug.WriteLine(RfDataSet[i,j].interval() + "");
                        }
                    }
                }

                StringBuilder sb = new System.Text.StringBuilder();
                //                String s = "";
                int gMax = 0;
                int gMin = 10000;
                int gAvrg = 0;
                int gSize = 0;
                int gSuccess = 0;

                for (int m = 0; m < SIZE_MOWAYS; m++)
                {
                    int sucess = 0;
                    int failed = 0;
                    int max = 0;
                    int min = 10000;
                    int avrg = 0;
                    int rSize = 0;
                    int prevAck = -1;

                    for (int i = 0; i < limit; i++)
                    {
                        int interval = RfDataSet[m, i].interval();
                        //s += interval + "," + RfDataSet[m, i].Ack + "   ";
                        if (interval > 0)
                        {
                            max = Math.Max(max, interval);
                            gMax = Math.Max(gMax, max);
                            avrg += interval;
                            gAvrg += interval;
                            min = Math.Min(min, interval);
                            gMin = Math.Min(gMin, min);
                            rSize++;
                            gSize++;
                            if (prevAck < RfDataSet[m, i].Ack)
                            {
                                prevAck = RfDataSet[m, i].Ack;
                                sucess++;
                                gSuccess++;
                            }
                            else
                            {
                                failed++;

                            }
                        }
                        else
                        {
                            failed++;

                        }

                    }
                    //System.Diagnostics.Debug.WriteLine("-- " + s + " --");
                    /* if (rSize != 0)
                     {
                         s += "(" + sucess.ToString() + "," + failed.ToString() + "," + (avrg / rSize) + "," + max.ToString() + "," + min.ToString() + ")";
                     }
                     sb.AppendLine(s);
                     s = "";*/

                    //System.Diagnostics.Debug.Flush();
                }
                if (gSize != 0)
                {

                    //s += this.mCount.ToString() + "" + gSuccess.ToString() + "," + gFailed.ToString() + "," + (gAvrg / gSize) + "," + gMax.ToString() + "," + gMin.ToString() + ")";
                    sb.AppendLine(String.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}",getCurrentSpeed(),getCurrentW8Time() ,mCount, gSuccess, (limit * mCount) - gSuccess, (gAvrg / gSize), gMax, gMin));


                }
                // System.Diagnostics.Debug.WriteLine("----");
                WriteTextFile.appendToLog(fileName, sb.ToString());
                this.Invoke((MethodInvoker)delegate
                {
                    //this.txtLog.Clear();
                    this.txtLog.AppendText(sb.ToString());
                    //btn_StopTest_Click(null, null);
                    // this.randoMizeCheckBoxes(mCount);
                });
            }
        }

        private void batchTest()
        {
            //for 1 moway till 4 moways; moway++
            //    for 1 package till 10 packages; package++
            //        for 0 w8Time till 200 w8Time; w8Time+=25
            //            for 50 sampling till 1000; sampling +=50
            string seed = (new Random()).Next().ToString();

            for (int sampling = 500; sampling >= 100; sampling -= 100)
            {
                for (int w8time = 150; w8time >= 0; w8time -= 50)
                {
                    for (int pckN = 1; pckN <= 5; pckN++)
                    {
                        for (int mowayIdE = 1; mowayIdE <= 4; mowayIdE++)
                        {
                            for (int pckg = 1; pckg <= pckN; pckg++)
                            {
                                Thread.Sleep(sampling);
                                for (int mowayId = 0; mowayId < mowayIdE; mowayId++)
                                {
                                    System.Diagnostics.Debug.WriteLine("ID=" + mowayId.ToString() + "   SAMP=" + sampling.ToString() + "   W8=" + w8time.ToString() + "   PCKG=" + pckg.ToString());
                                    System.Diagnostics.Debug.Flush();
                                    //*****************************
                                    this.RfDataSet[mowayId, pckg - 1].TimeSent = Environment.TickCount;
                                    this.rfusb.Send(mowayId + 27, this.RfDataSet[mowayId, pckg - 1].toByteArray());
                                    //*****************************
                                    Thread.Sleep(w8time);
                                }

                                this.Invoke((MethodInvoker)delegate
                                {
                                    StringBuilder sb = new StringBuilder();
                                    sb.AppendLine("SAMPLING=" + sampling);
                                    sb.AppendLine("W8TIME=" + w8time);
                                    sb.AppendLine("PACKS=" + pckN);
                                    sb.AppendLine("MOWAYS=" + mowayIdE);
                                    this.updateLog("file", this.getCurrentPackSize());
                                });

                            }

                        }

                    }

                }
            }
        }

        private void sendPackt(object source, ElapsedEventArgs e)
        {
            var lseqToSend = seqToSend;
            int packtSize = 0;
            int speed = 0;
            int w8Time = 0;

            this.Invoke((MethodInvoker)delegate
               {
                   packtSize = this.getCurrentPackSize();
                   speed = this.getCurrentSpeed();
                   w8Time = this.getCurrentW8Time();


                   if (lseqToSend < packtSize && lseqToSend < SIZE_TEST)
                   {

                       for (int m = 0; m < SIZE_MOWAYS; m++)
                       {
                           if ((m == 0 && chk_M1.Checked)
                               || m == 1 && chk_M2.Checked
                               || m == 2 && chk_M3.Checked
                               || m == 3 && chk_M4.Checked)
                           {
                               this.RfDataSet[m, lseqToSend].TimeSent = Environment.TickCount;
                               this.rfusb.Send(m + 27, this.RfDataSet[m, lseqToSend].toByteArray());
                               Thread.Sleep(w8Time);
                           }

                       }
                       seqToSend++;

                   }
                   else
                   {
                       this.myTimer3.Stop();
                       myTimer3.Enabled = false;
                       Thread.Sleep(1000);

                       updateLog(@"Sampling\" + speed.ToString() + @"\WaitingTime\" + w8Time.ToString() + @"\#Moway\" + this.mCount.ToString() + @"\statistics", packtSize);
                       btn_StopTest_Click(null, null);
                       this.updateParameters();


                   }

               });
        }

        private class RFData
        {
            int _data;
            public int Data
            {
                get { return _data; }
                set { _data = value; }
            }

            int _timeSent;
            public int TimeSent
            {
                get { return _timeSent; }
                set { _timeSent = value; }
            }
            int _seqNum;
            public int SeqNum
            {
                get { return _seqNum; }
                set { _seqNum = value; }
            }
            int _timeAck;
            public int TimeAck
            {
                get { return _timeAck; }
                set { _timeAck = value; }
            }

            int _ack;
            public int Ack
            {
                get { return _ack; }
                set { _ack = value; }
            }

            int _echoRequest;
            public int EchoRequest
            {
                get { return _echoRequest; }
                set { _echoRequest = value; }
            }

            public byte[] toByteArray()
            {
                return new byte[] { Convert.ToByte(this._data), Convert.ToByte(this._seqNum), Convert.ToByte(this._echoRequest) };
            }

            public int interval()
            {
                if (this.TimeAck > 0)
                {
                    return Math.Abs(this._timeAck - this._timeSent);
                }
                else
                {
                    return 0;
                }
            }

            public RFData(int seqN)
            {
                this._ack = -1;
                this._data = generator.Next(256);
                this._seqNum = seqN;
                this._echoRequest = 10;
            }
        }

        private void rdb_CtrlOff_CheckedChanged(object sender, EventArgs e)
        {
            this.gb_Transmit.Enabled = false;
            this.transmission = false;
        }

        private void rdb_CtrlOn_CheckedChanged(object sender, EventArgs e)
        {
            this.gb_Transmit.Enabled = true;
            this.transmission = true;
        }

        private void btn_StopTest_Click(object sender, EventArgs e)
        {
            seqToSend = 0;
            myTimer3.Stop();
            btn_StartTest.Enabled = true;
            btn_StopTest.Enabled = false;
            txt_MiliIntervalTest.Enabled = true;
            cb_Speed.Enabled = true;
            txt_LenghtTest.Enabled = true;
            cb_Pckts.Enabled = true;
            txt_W8Time.Enabled = true;
            cb_W8Time.Enabled = true;
        }

        private void btn_StartTest_Click(object sender, EventArgs e)
        {
            //T#    S	W//	PKT POK PNOK    AVRG  MAX MIN

            this.rfusb.resetMatrix();
            //Start from 0
            seqToSend = 0;

            //Reset Timer
            myTimer3.Interval = this.getCurrentSpeed();
            myTimer3.Enabled = true;
            myTimer3.Start();

            //Disable Options 
            btn_StartTest.Enabled = false;
            btn_StopTest.Enabled = true;
            txt_MiliIntervalTest.Enabled = false;
            cb_Speed.Enabled = false;
            txt_LenghtTest.Enabled = false;
            cb_Pckts.Enabled = false;
            txt_W8Time.Enabled = false;
            cb_W8Time.Enabled = false;
        }

        private void updateParameters()
        {

            this.Invoke((MethodInvoker)delegate
               {
                   int w8t = this.getCurrentW8Time();
                   if (w8t < 100)
                   {
                       w8t += 25;
                       this.setCurrentW8Time(w8t);
                      // this.randoMizeCheckBoxes(mCount);
                       btn_StartTest_Click(null, null);
                   }
                   else
                   {
                       this.setCurrentW8Time(0);
                   }
               });
        }

        private void setCurrentW8Time(int value)
        {
            this.txt_W8Time.Text = value.ToString();
            this.cb_W8Time.SelectedItem = value.ToString();
        }

        private void randoMizeCheckBoxes()
        {
            this.randoMizeCheckBoxes((new Random()).Next(1, 4));
        }

        private void randoMizeCheckBoxes(int checkedValues)
        {
            this.chk_M1.Checked = false;
            this.chk_M2.Checked = false;
            this.chk_M3.Checked = false;
            this.chk_M4.Checked = false;

            bool[] booleans = new bool[] { false, false, false, false };
            checkedValues = Math.Min(checkedValues, booleans.Length);

            for (int i = 0; i < checkedValues; i++)
            {
                booleans[i] = true;
            }
            Random rand = new Random();
            bool tempSwap;
            int index;
            for (int i = 0; i < booleans.Length; i++)
            {
                index = rand.Next(booleans.Length);
                tempSwap = booleans[i];
                booleans[i] = booleans[index];
                booleans[index] = tempSwap;
            }

            this.chk_M1.Checked = booleans[0];
            this.chk_M2.Checked = booleans[1];
            this.chk_M3.Checked = booleans[2];
            this.chk_M4.Checked = booleans[3];

            this.mCount = checkedValues;
        }

        private int getCurrentSpeed()
        {
            return Convert.ToInt16(this.cb_Speed.SelectedItem);
        }

        private int getCurrentPackSize()
        {
            return Convert.ToInt16(this.cb_Pckts.SelectedItem);
        }

        private int getCurrentW8Time()
        {
            return Convert.ToInt16(this.cb_W8Time.SelectedItem);
        }

        private void btn_UpdateLog_Click(object sender, EventArgs e)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("SAMPLING=" + this.getCurrentSpeed().ToString());
            sb.AppendLine("W8TIME=" + this.getCurrentW8Time().ToString());
            sb.AppendLine("PACKS=" + this.getCurrentPackSize().ToString());
            sb.AppendLine("MOWAYS=" + this.getCurrentSpeed().ToString());
            this.updateLog(sb.ToString(), 5);
        }

        private void btn_BatchTest_Click(object sender, EventArgs e)
        {
            this.randoMizeCheckBoxes();
        }

        private void btn_TestFile_Click(object sender, EventArgs e)
        {
            WriteTextFile.WriteToFile("teste");
        }

        private void chk_MX_CheckedChanged(object sender, EventArgs e)
        {
            if (((System.Windows.Forms.CheckBox)sender).Checked)
            {
                mCount++;
            }
            else
            {
                mCount--;
            }
        }

        private void btn_Clear_Click(object sender, EventArgs e)
        {
            this.txtLog.Clear();
        }
    }
}
